WebGL 纹理创建与 UV 映射
一句话结论:纹理创建负责把图片搬进显存,UV 映射负责告诉每个顶点取图片的哪一块,两者对接正确才能贴出正确图案。
纹理对象创建与图片上传
是什么
纹理对象是显存中存放图像数据的资源,创建流程为生成纹理、绑定目标、写入像素、设置采样参数。上传后由片元着色器中的采样器读取。
解决什么问题
它解决逐像素颜色数据来源问题。用顶点颜色表达细节需要海量顶点,而一张纹理用一次上传就能给整个表面提供细致图案,极大降低了顶点数量与内存占用。
为什么这样做
采样是 GPU 的固定硬件能力,纹理存储按二维局部性优化,相邻像素读取几乎无额外开销。把图像数据放进显存后,每帧只需绑定引用,不需要重复传输,因此贴图是渲染最经济的细节表达方式。
怎么用
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// 图片加载完成后再上传
const img = new Image();
img.onload = () => {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); // 纠正 Y 轴方向
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
};
img.src = 'texture.png';
// 绘制前绑定纹理单元
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(gl.getUniformLocation(program, 'uSampler'), 0);
常见坑
- 图片未加载就调用 texImage2D → 原因:异步加载未完成 → 处理:在 onload 回调中上传
- 尺寸非 2 的幂却启用 mipmap → 原因:WebGL 1.0 限制 → 处理:改用 CLAMP_TO_EDGE 加非 mipmap 过滤,或补到 2 的幂
- 忘记设置 MIN_FILTER → 原因:默认需要 mipmap → 处理:显式设为 LINEAR 或 NEAREST
- 采样器 uniform 未赋值 → 原因:默认绑定 0 号单元但纹理绑在别的单元 → 处理:用 uniform1i 明确指定
纹理坐标与 UV 映射
是什么
UV 坐标是把纹理图像映射到模型表面的二维坐标,取值范围一般是 0 到 1,写在顶点属性中,经 varying 插值后由片元着色器用于采样。
解决什么问题
它解决"贴图的哪一部分对应模型的哪个位置"的问题。没有 UV,GPU 不知道该取纹理的哪一点;有了 UV,同一张贴图可以按不同方式铺在不同模型上,也能只取其中一部分做图集采样。
为什么这样做
UV 用归一化坐标表达,与图像实际分辨率解耦。更换不同分辨率的贴图时无需修改顶点数据,图集复用也只需调整 UV 区间,这让资源替换与合批变得非常廉价。
怎么用
// 顶点数据:位置 3 个 float + UV 2 个 float,交错存放
const vertices = new Float32Array([
// x, y, z, u, v
-0.5, -0.5, 0, 0, 0,
0.5, -0.5, 0, 1, 0,
0.5, 0.5, 0, 1, 1,
-0.5, 0.5, 0, 0, 1,
]);
const STRIDE = 5 * 4;
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, STRIDE, 0);
gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, STRIDE, 3 * 4);
// 片元着色器采样
precision mediump float;
uniform sampler2D uSampler;
varying vec2 vTexCoord;
void main() {
gl_FragColor = texture2D(uSampler, vTexCoord);
}
常见坑
- 贴图上下颠倒 → 原因:图片与纹理坐标 Y 轴方向相反 → 处理:启用 UNPACK_FLIP_Y_WEBGL,或在着色器中翻转 v
- UV 顺序与顶点不一致 → 原因:交错布局的偏移写错 → 处理:核对步长与偏移字节数
- 模型出现拉伸 → 原因:UV 未按面积分配 → 处理:展开 UV 时保持面积比例
- 图集采样串到相邻图标 → 原因:UV 边界与插值溢出 → 处理:内缩半像素或加边距
要点总结
- 纹理四步:创建、绑定、texImage2D 上传、设置过滤与环绕参数
- 图片必须在 onload 之后上传,并按需开启 UNPACK_FLIP_Y_WEBGL
- UV 归一化到 0 到 1,与分辨率解耦,便于换图与图集合批
- 采样器 uniform 用 uniform1i 指定纹理单元序号
- 交错布局中 UV 的偏移是 12 字节(位置占 3 个 float)
